#include "clkCollada.h"

clkCollada::clkCollada()
{

}

clkCollada::clkCollada(string a_path)
{
	openDoc(a_path);
}

clkCollada::~clkCollada()
{
	closeDoc();
}

void clkCollada::openDoc(string a_path)
{
	m_doc.open(a_path);
}

void clkCollada::closeDoc()
{
	m_doc.close();
}

bool clkCollada::loadVertices(clkColladaScene* a_scene)
{
	if(!a_scene) return false;

	clkXmlElement* root = m_doc.getRoot();
	if(!root) return false;

	clkXmlElement* libgeo = root->getChild("library_geometries");
	if(!libgeo) return false;

	vector<clkXmlElement*> geometries = libgeo->getChildren("geometry");
	if(geometries.size() == 0) return false;

	for(int i=0;i<(int)geometries.size();i++)
	{
		clkXmlElement* geometry = geometries[i];

		clkXmlElement* mesh = geometry->getChild("mesh");
		if(!mesh) return false;

		clkXmlElement* vertices = mesh->getChild("vertices");
		if(!vertices) return false;

		clkXmlElement* triangles = mesh->getChild("triangles");
		if(!triangles) return false;

		clkXmlElement* parray = triangles->getChild("p");
		if(!parray) return false;

		vector<clkXmlElement*> inputs = triangles->getChildren("input");
		vector<clkXmlElement*> vinputs = vertices->getChildren("input");
		for(int j=0;j<(int)vinputs.size();j++)
			inputs.push_back(vinputs[j]);
		if(inputs.size() == 0) return false;

		vector<clkXmlElement*> sources = mesh->getChildren("source");
		if(sources.size() == 0) return false;

		//buffers and offsets per buffer
		vector<float> positions, normals, tangents, binormals, texcoords;
		int posOff = 0, nrmOff = 0, tanOff = 0, binOff = 0, texOff = 0;

		//loop through and cache strings into buffers
		for(int j=0;j<(int)sources.size();j++)
		{
			for(int k=0;k<(int)inputs.size();k++)
			{
				clkXmlElement* source = sources[j];
				clkXmlElement* input = inputs[k];

				clkXmlElement* farray = source->getChild("float_array");
				if(!farray) return false;

				string sourceName = source->getAttributeData("name");
				string inputName = input->getAttributeData("source");
				string semantic = input->getAttributeData("semantic");

				if(inputName.find(sourceName) == string::npos)
					continue;

				string offset = input->getAttributeData("offset");

				//prepare stream with "source" float array
				string sdFarray = farray->getData();
				istringstream stream(sdFarray);
				if(!strcmp("POSITION",semantic.data()) && positions.empty())
				{
					//cache offset; default to ZERO if none provided
					posOff = (!offset.empty())?atoi(offset.c_str()):0;

					//stream the string into the POSITION buffer
					copy(istream_iterator<float>(stream),
						istream_iterator<float>(),
						back_inserter(positions));
				}
				else if(!strcmp("NORMAL",semantic.data()) && normals.empty())
				{
					//cache offset; default to ZERO if none provided
					nrmOff = (!offset.empty())?atoi(offset.c_str()):0;

					//stream the string into the NORMAL buffer
					copy(istream_iterator<float>(stream),
						istream_iterator<float>(),
						back_inserter(normals));
				}
				else if(!strcmp("TANGENT",semantic.data()) && tangents.empty())
				{
					//cache offset; default to ZERO if none provided
					tanOff = (!offset.empty())?atoi(offset.c_str()):0;

					//stream the string into the TANGENT buffer
					copy(istream_iterator<float>(stream),
						istream_iterator<float>(),
						back_inserter(tangents));
				}
				else if(!strcmp("BINORMAL",semantic.data()) && binormals.empty())
				{
					//cache offset; default to ZERO if none provided
					binOff = (!offset.empty())?atoi(offset.c_str()):0;

					//stream the string into the BINORMAL buffer
					copy(istream_iterator<float>(stream),
						istream_iterator<float>(),
						back_inserter(binormals));
				}
				else if(!strcmp("TEXCOORD",semantic.data()) && texcoords.empty())
				{
					//cache offset; default to ZERO if none provided
					texOff = (!offset.empty())?atoi(offset.c_str()):0;

					//stream the string into the TEXCOORD buffer
					copy(istream_iterator<float>(stream),
						istream_iterator<float>(),
						back_inserter(texcoords));
				}
			}
		}

		//stream the string of "p" array into the buffer
		vector<int> indices;
		string sdParray = parray->getData();
		istringstream stream(sdParray);
		copy(istream_iterator<int>(stream),
			istream_iterator<int>(),
			back_inserter(indices));

		//find the minimal INDEX count
		int icount = ((posOff>nrmOff)
			?posOff:(nrmOff>tanOff)
			?nrmOff:(tanOff>binOff)
			?tanOff:(binOff>texOff)
			?binOff:texOff)+1;

		//create subset mesh and loop through to build
		//vector<clkColladaVert>* vertList = new vector<clkColladaVert>;
		clkColladaMesh* cmesh = new clkColladaMesh;
		a_scene->m_meshes.push_back(cmesh);
		for(int j=0;j<((int)indices.size());j+=icount)
		{
			clkColladaVert vert;
			memset(&vert,0,sizeof(vert));

			//build position vertex info if any
			if(!positions.empty())
			{
				vert.px = positions[(indices[j+posOff]*3)];
				vert.py = positions[(indices[j+posOff]*3)+1];
				vert.pz = -positions[(indices[j+posOff]*3)+2];
			}

			//build normal vertex info if any
			if(!normals.empty())
			{
				vert.nx = normals[(indices[j+nrmOff]*3)];
				vert.ny = normals[(indices[j+nrmOff]*3)+1];
				vert.nz = -normals[(indices[j+nrmOff]*3)+2];
			}

			//build tangent vertex info if any
			if(!tangents.empty())
			{
				vert.tx = tangents[(indices[j+tanOff]*3)];
				vert.ty = tangents[(indices[j+tanOff]*3)+1];
				vert.tz = -tangents[(indices[j+tanOff]*3)+2];
			}

			//build binormal vertex info if any
			if(!binormals.empty())
			{
				vert.bx = binormals[(indices[j+binOff]*3)];
				vert.by = binormals[(indices[j+binOff]*3)+1];
				vert.bz = -binormals[(indices[j+binOff]*3)+2];
			}

			//build texcoord vertex info if any
			if(!texcoords.empty())
			{
				vert.ux = texcoords[(indices[j+texOff]*2)];
				vert.uy = texcoords[(indices[j+texOff]*2)+1];
			}

			//add to mesh vertex list
			a_scene->m_meshes[i]->m_verts.push_back(vert);
		}
	}

	return true;
}